/**
 * Gardner，游戏框架主类，轻量级游戏UI框架，兼容横屏竖屏
 */
export class Gardener {

	/**
	 * 单例对象
	 */
	private static __instance: Gardener;

	/**
	 * 全局Main对象
	 */
	private __mainContainer: cc.Node;
	/**
	 * View层容器，游戏最底层
	 */
	private __viewContainer: cc.Node;
	/**
	 * Window层容器，Sprite类型，可以设置底色，在View层之上，导航层之下
	 */
	private __windowContainer: cc.Node;
	/**
	 * 导航层，在View,Window层之上，popup层之下
	 */
	private __navigatorContainer: cc.Node;
	/**
	 * Popup层，在导航层之上，message层之下
	 */
	private __popupContainer: cc.Node;
	/**
	 * messagebox 层，Popup层之上
	 */
	private __messageContainer: cc.Node;
	/**
	 * 全局高度
	 */
	public __stageHeight: number;
	/**
	 * 全局宽度
	 */
	public __stageWidth: number;
	/**
	 * gardener 全局配置
	 */
	public __configuration: any;

	/**
	 * 字符串模板json对象
	 */
	private __stringResourceJson: any = {};

	// window历史列表，不包含当前当前打开的window
	private __windowHistoryList: Array < cc.Node > = new Array < cc.Node > ();


	private constructor(main: cc.Node, config ? : any) {
		this.__mainContainer = main;

		// 初始化各层容器
		this.__initContainers(main, config);
	}

	/**
	 * 初始化所有容器
	 */
	private __initContainers(main: cc.Node, config: any) {

		this.__configuration = config;

		this.__stageHeight = config.stageHeight;
		this.__stageWidth = config.stageWidth;

		let __viewContainer: cc.Node = new cc.Node();
		__viewContainer.width = config.stageWidth;
		__viewContainer.height = config.stageHeight;
		this.__viewContainer = __viewContainer;
		main.addChild(this.__viewContainer);

		let __windowContainer = new cc.Node();
		__windowContainer.width = config.stageWidth;
		__windowContainer.height = config.stageHeight;
		let windowGraphics: cc.Graphics = __windowContainer.addComponent(cc.Graphics);
		windowGraphics.rect(-__windowContainer.width * .5, -__windowContainer.height * .5, __windowContainer.width, __windowContainer.height);
		windowGraphics.fillColor = config.windowContainerMaskColor;
		windowGraphics.fill();
		__windowContainer.addComponent(cc.BlockInputEvents);
		__windowContainer.active = false;
		main.addChild(__windowContainer);
		this.__windowContainer = __windowContainer;

		if (config.navigatorContainerAvailable === true) {
			let __navigatorContainer = new cc.Node();
			__navigatorContainer.width = config.stageWidth;
			__navigatorContainer.height = config.stageHeight;
			// __navigatorContainer.touchEnabled = false;
			// __navigatorContainer.touchChildren = true;
			main.addChild(__navigatorContainer);
			this.__navigatorContainer = __navigatorContainer;
		}

		let __popupContainer = new cc.Node();
		__popupContainer.width = config.stageWidth;
		__popupContainer.height = config.stageHeight;
		let popupGraphics: cc.Graphics = __popupContainer.addComponent(cc.Graphics);
		popupGraphics.rect(-__popupContainer.width * .5, -__popupContainer.height * .5, __popupContainer.width, __popupContainer.height);
		popupGraphics.fillColor = config.popupContainerMaskColor;
		popupGraphics.fill();
		__popupContainer.addComponent(cc.BlockInputEvents);
		__popupContainer.active = false;
		main.addChild(__popupContainer);
		this.__popupContainer = __popupContainer;

		let __messageContainer = new cc.Node();
		__messageContainer.width = config.stageWidth;
		__messageContainer.height = config.stageHeight;
		let messageGraphics: cc.Graphics = __messageContainer.addComponent(cc.Graphics);
		messageGraphics.rect(-__messageContainer.width * .5, -__messageContainer.height * .5, __messageContainer.width, __messageContainer.height);
		messageGraphics.fillColor = config.messageContainerMaskColor;
		messageGraphics.fill();
		__messageContainer.addComponent(cc.BlockInputEvents);
		__messageContainer.active = false;
		__messageContainer.on(cc.Node.EventType.TOUCH_END, this.__onMessageContainerBg_Tap, this);
		main.addChild(__messageContainer);
		this.__messageContainer = __messageContainer;
	}

	/**
	 * 初始化Gardener
	 * main 白鹭项目启动类 Main
	* @param main 白鹭项目启动类 Main
	* @param config {
			navigatorContainerAvailable?: Boolean,	// 是否启用导航层
			stringResName?: string,					// 全局本地化文本字符串配置文件，json格式， 通过gardener.getString(key)来获取
			orientation?: number,					// App窗口方向，默认是项目设定的方向
			stageWidth?: number,					// 自定义游戏窗口宽度，默认是全屏窗口宽度
			stageHeight?: number,					// 自定义游戏窗口高度，默认是全屏窗口高度
			windowContainerMaskColor?: cc.Color,		// Window层遮罩颜色，默认是黑色
			popupContainerMaskColor?: cc.Color,		// Popup层遮罩颜色，默认是黑色
			messageContainerMaskColor?: cc.Color,		// MessageBox层遮罩颜色，默认
			popupPositionHori?: number,				// Popup对象全局水平位置，默认是中间0%
			popupPositionVert?: number,				// Popup对象全局垂直位置，默认是中间0%
			allowMultipleMessageBoxes?: Boolean,	// 是否允许MessageBox可以弹出多层，每层独立，默认是true，如果为false，新弹出的messagebox会关闭前一个
			showMultipleMessageBoxesOffset?: number	// 当allowMultipleMessageBoxes=true的时候，后一个相比前一个messagebox是否有错位显示，默认是10}
	*/
	public static init(main: cc.Node, config ? : {
		navigatorContainerAvailable ? : Boolean,
		stringResName ? : string,
		orientation ? : number,
		stageWidth ? : number,
		stageHeight ? : number,
		windowContainerMaskColor ? : cc.Color,
		popupContainerMaskColor ? : cc.Color,
		messageContainerMaskColor ? : cc.Color,
		popupPositionHori ? : number,
		popupPositionVert ? : number,
		allowMultipleMessageBoxes ? : Boolean,
		showMultipleMessageBoxesOffset ? : number
	}) {

		// 初始化参数
		config = config || {};
		config.orientation = config.orientation || cc.view["_orientation"];

		config.stageHeight = config.stageHeight || cc.winSize.height;
		config.stageWidth = config.stageWidth || cc.winSize.width;

		config.windowContainerMaskColor = config.windowContainerMaskColor || new cc.Color(0, 0, 0, 25);
		config.popupContainerMaskColor = config.popupContainerMaskColor || new cc.Color(0, 0, 0, 25);
		config.messageContainerMaskColor = config.messageContainerMaskColor || new cc.Color(0, 0, 0, 25);
		config.popupPositionHori = config.popupPositionHori || 0;
		config.popupPositionVert = config.popupPositionVert || 0;

		config.allowMultipleMessageBoxes = config.allowMultipleMessageBoxes === undefined ? true : config.allowMultipleMessageBoxes;
		config.showMultipleMessageBoxesOffset = config.showMultipleMessageBoxesOffset || 10;

		config.navigatorContainerAvailable = config.navigatorContainerAvailable === undefined ? false : config.navigatorContainerAvailable;

		Gardener.__instance = new Gardener(main, config);

		config.stringResName && cc.loader.loadRes(config.stringResName, function (err, res) {
			Gardener.__instance.__stringResourceJson = res.json;
		})
	}

	/**
	 * 打开一个View
	 * @param view cc.Node 打开的对象
	 * @param showAnime Boolean 是否显示切换View的动画
	 */
	private __openView(view: cc.Node, showAnime: Boolean = true): Boolean {
		this.__closeWindow(true);
		let container: cc.Node = this.__viewContainer;
		if (container.childrenCount > 1) {
			let com = view.getComponent(GardenerView);
			com && com.dispose();
			return false;
		}

		let lastLayer: cc.Node = container.childrenCount > 0 ? container.children[0] : undefined;

		if (lastLayer && lastLayer.name == view.name) {
			let com = view.getComponent(GardenerView);
			com && com.dispose();
			return false;
		}

		if (!showAnime) {
			view.setPosition(0, 0);
			let fn = () => {
				let com = view.getComponent(GardenerView);
				com && com.display();
				lastLayer && lastLayer.parent && lastLayer.removeFromParent();
				setTimeout(() => {
					let com = lastLayer.getComponent(GardenerView);
					com && com.dispose();
				}, 30);
			}
			container.once(cc.Node.EventType.CHILD_ADDED, fn, view);
			container.addChild(view);
		} else {
			view.setPosition(0, -this.__stageHeight);
			let fn = () => {
				setTimeout(() => {
					cc.tween(view).to(.3, {
						position: cc.v3(0, 0, 0)
					}, {
						easing: 'circOut'
					}).call(() => {
						let com = view.getComponent(GardenerView);
						com && com.display();
						if (lastLayer) {
							lastLayer.parent && lastLayer.removeFromParent();
							let com = lastLayer.getComponent(GardenerView);
							com && com.dispose();
						}
					}).start();
					if (lastLayer && this.__configuration.orientation >= cc.macro.ORIENTATION_LANDSCAPE) {
						cc.tween(lastLayer).to(.1, {
							position: cc.v3(lastLayer.x + 20, lastLayer.y - 20, 0)
						}).start();
					}
				}, .05);
			}
			container.once(cc.Node.EventType.CHILD_ADDED, fn, view);
			container.addChild(view);
		}
		return true;
	}

	/**
	 * 清理所有window历史
	 */
	private __clearWindowHistory() {
		let windowList = this.__windowHistoryList.splice(0);
		windowList.forEach((window) => {
			let com = window.getComponent(GardenerWindow);
			com && com.dispose();
		});
		windowList = null;
	}

	/**
	 * 打开一个window
	 * @param window cc.Node
	 * @param savePreWindow Boolean 是否缓存上一个打开的window
	 * @param isPreWindow Boolean 是否是打开之前缓存的window
	 */
	private __openWindow(window: cc.Node, savePreWindow: Boolean = false, isPreWindow: Boolean = false): Boolean {
		let container: cc.Node = this.__windowContainer;
		container.active = true;

		if (container.childrenCount > 1) {
			let com = window.getComponent(GardenerWindow);
			com && com.dispose();
			return false;
		}

		let lastWindow: cc.Node = container.childrenCount > 0 ? < cc.Node > container.children[0] : undefined;
		if (lastWindow && lastWindow.name == window.name) {
			let com = window.getComponent(GardenerWindow);
			com && com.dispose();
			return false;
		}

		let delay: number = lastWindow ? .2 : .08;

		if (lastWindow && savePreWindow) {
			this.__windowHistoryList.push(lastWindow);
		}

		let lastWindowDesX: number;
		let lastWindowDesY: number;
		if (this.__configuration.orientation >= cc.macro.ORIENTATION_LANDSCAPE) {
			if (isPreWindow) {
				window.y = (this.__stageHeight + window.height) * .5;
				window.x = (this.__stageWidth + window.width) * .5;
			} else {
				window.y = -(this.__stageHeight + window.height) * .5;
				window.x = -(this.__stageWidth + window.width) * .5;
			}
			if (lastWindow) {
				lastWindowDesY = (this.__stageHeight + window.height) * .5;
				lastWindowDesX = (this.__stageWidth + window.width) * .5;
			}
		} else {
			if (isPreWindow) {
				window.y = 0;
				window.x = -(this.__stageWidth + window.width) * .5;
			} else {
				window.y = 0;
				window.x = (this.__stageWidth + window.width) * .5;
			}
			if (lastWindow) {
				lastWindowDesX = -(this.__stageWidth + window.width) * .5;
				lastWindowDesY = 0;
			}
		}
		if (lastWindow) {
			lastWindow.stopAllActions();
			cc.tween(lastWindow).to(delay, {
				position: cc.v3(lastWindowDesX, lastWindowDesY, 0)
			}, {
				easing: 'circIn'
			}).call(() => {
				lastWindow.parent && lastWindow.removeFromParent();
			}).start();
		}

		let fn = () => {
			let desX = (container.width - window.width) * .5;
			let desY = (container.height - window.height) * .5;
			cc.tween(window).delay(delay).to(.3, {
				position: cc.v3(desX, desY)
			}, {
				easing: 'circOut'
			}).call(() => {
				let com = window.getComponent(GardenerWindow);
				com && com.display();

				if (!savePreWindow) {
					if (lastWindow) {
						lastWindow.parent && lastWindow.removeFromParent();
						let com = lastWindow.getComponent(GardenerWindow);
						com && com.dispose();
					}
					this.__clearWindowHistory();
				}
			}).start();
		}
		container.once(cc.Node.EventType.CHILD_ADDED, fn, window);
		container.addChild(window);
	}

	/** 
	 * 关闭Window
	 * @param closeAll Boolean 是否一同关闭window的缓存历史
	 */
	private __closeWindow(closeAll: Boolean = false): void {
		let container: cc.Node = this.__windowContainer;
		let windowList = this.__windowHistoryList;
		if (container.childrenCount > 0) {
			let currentWindow: cc.Node = container.childrenCount > 0 ? container.children[0] : undefined;
			let desX: number;
			let desY: number;
			if (this.__configuration.orientation >= cc.macro.ORIENTATION_LANDSCAPE) {
				if (!closeAll && windowList.length > 0) {
					desY = -(this.__stageHeight + currentWindow.height) * .5;
					desX = -(this.__stageWidth + currentWindow.width) * .5;
				} else {
					desY = (this.__stageHeight + currentWindow.height) * .5;
					desX = (this.__stageWidth + currentWindow.width) * .5;
				}
			} else {
				if (!closeAll && windowList.length > 0) {
					desY = 0;
					desX = (this.__stageWidth + currentWindow.width) * .5;
				} else {
					desY = 0;
					desX = -(this.__stageWidth + currentWindow.width) * .5;
				}
			}

			cc.tween(currentWindow).to(.2, {
				position: cc.v3(desX, desY)
			}, {
				easing: 'circIn'
			}).call(() => {
				container.active = false;
				cc.tween(currentWindow).stop();
				currentWindow.parent && currentWindow.removeFromParent();

				let com = currentWindow.getComponent(GardenerWindow);
				com && com.dispose();
				if (closeAll) {
					this.__clearWindowHistory();
				} else {
					if (windowList.length > 0) {
						let preWindow = windowList.pop();
						this.__openWindow(preWindow, false, true);
					}
				}
			}).start();
		} else {
			container.active = false;
		}
	}

	/*
	 * 打开Popup
	 */
	private __openPopup(popup: cc.Node, showAnime: Boolean = true): void {
		this.__closePopup(true);
		let container: cc.Node = this.__popupContainer;
		container.active = true;

		popup.x = this.__stageWidth * this.__configuration.popupPositionHori;
		popup.y = this.__stageHeight * (this.__configuration.popupPositionVert + .13);
		popup.opacity = 0;
		let fn = () => {
			const com = popup.getComponent(GardenerPopup);
			if (showAnime) {
				cc.tween(popup).delay(.08).to(.3, {
					position: cc.v3(popup.x, this.__stageHeight * this.__configuration.popupPositionVert),
					opacity: 255
				}, {
					easing: 'sineOut'
				}).call(() => {
					com && com.display();
				}).start();
			} else {
				popup.opacity = 255;
				com && com.display();
			}
			com && com.addedToStageHandler && com.addedToStageHandler();
		}
		container.once(cc.Node.EventType.CHILD_ADDED, fn, this);

		container.addChild(popup);
	}

	/**
	 * 关闭当前popup
	 * @param immediate Boolean 立即关闭，不显示动画，默认false
	 */
	private __closePopup(immediate: Boolean = false): void {
		let container: cc.Node = this.__popupContainer;
		if (container.childrenCount > 0) {
			let lastPopup: cc.Node = container.childrenCount > 0 ? container.children[0] : undefined;
			if (immediate) {
				cc.tween(lastPopup).stop();
				container.removeAllChildren();
				let com = lastPopup.getComponent(GardenerPopup);

				com && com.removedFromeStageHandler && com.removedFromeStageHandler();
				com && com.dispose();
				container.active = false;
			} else {
				cc.tween(lastPopup).to(.2, {
					position: cc.v3(lastPopup.x, this.__stageHeight * (this.__configuration.popupPositionVert + .13)),
					opacity: 0
				}, {
					easing: 'sineIn'
				}).call(() => {
					container.removeAllChildren();
					container.active = false;
					let com = lastPopup.getComponent(GardenerPopup);
					
					com && com.removedFromeStageHandler && com.removedFromeStageHandler();
					com && com.dispose();
				}).start();
			}
		} else {
			container.active = false;
		}
	}
	/**
	 * 显示飘字文本
	 * @param tip IFloatTip
	 */
	private __showFloatTip(tip: cc.Node, x ? : number, y ? : number) {
		tip.opacity = 0;
		tip.x = x || 0;
		tip.y = y || 0;
		this.__mainContainer.addChild(tip);
		cc.tween(tip).to(.2, {
			opacity: 255
		}).delay(.5).to(1, {
			position: cc.v3(tip.x, tip.y + 100, 0)
		}).to(.5, {
			position: cc.v3(tip.x, tip.y + 150, 0),
			opacity: 0
		}).call(() => {
			tip.destroy();
		}).start();
	}

	/**
	 * 弹出messagebox
	 * @param messageBox IMessageBox
	 * @param closeOthers Boolean 打开的同时是否关闭其他当前打开
	 */
	private __showMessageBox(messageBox: cc.Node, closeOthers: Boolean = false, customDisplay:Boolean = false) {
		let container = this.__messageContainer;
		container.active = true;
		if (container.childrenCount > 0 && (closeOthers || !this.__configuration.allowMultipleMessageBoxes)) {
			let msgs: Array < cc.Node > = container.children;
			container.removeAllChildren();
			msgs.forEach((msg) => {
				cc.tween(msg).stop();
				let com = msg.getComponent(GardenerMessageBox);
				com && com.dispose();
			});
			msgs = null;
		}

		let offset = ( < any > messageBox).allowMultipleOffsetPosition ? container.childrenCount * this.__configuration.showMultipleMessageBoxesOffset : 0;
		messageBox.x += offset;
		messageBox.y += offset;
		container.addChild(messageBox);
		if (customDisplay) {
			Promise.resolve().then(
				() => {
					let com = messageBox.getComponent(GardenerMessageBox);
					com && com.display();
				}
			);
		} else {
			messageBox.scaleX = messageBox.scaleY = 0;
			cc.tween(messageBox).delay(.03).to(.25, {
				scaleX: 1,
				scaleY: 1
			}, {
				easing: 'backOut'
			}).call(() => {
				let com = messageBox.getComponent(GardenerMessageBox);
				com && com.display();
			}).start();
		}
	}
	/**
	 * 关闭指定的messagebox
	 * @param target IMessageBox
	 * @param closeAll Boolean  关闭全部
	 */
	private __closeMessageBox(target: cc.Node, closeAll: Boolean = false) {
		let container = this.__messageContainer;
		if (!target && container.childrenCount <= 0) {
			container.active = false;
			return;
		}
		if (closeAll) {
			if (container.childrenCount > 0) {
				let msgs: Array < cc.Node > = container.children;
				container.removeAllChildren();
				msgs.forEach((msg) => {
					cc.tween(msg).stop();
					let com = msg.getComponent(GardenerMessageBox);
					com && com.dispose();
				});
				msgs = null;
			}
		} else if (target) {
			cc.tween(target).stop();
			target.parent && target.removeFromParent()
			let com = target.getComponent(GardenerMessageBox);
			com && com.dispose();
		}
		if (container.childrenCount <= 0) {
			container.active = false;
		}
	}

	/**
	 * 点击messagebox背景触发事件
	 */
	private __onMessageContainerBg_Tap(e: cc.Event) {
		let container = this.__messageContainer;
		if (e.target != container) {
			return;
		}
		if (container.childrenCount > 0) {
			let target = container.children[container.childrenCount - 1];
			let com = target.getComponent(GardenerMessageBox);
			if (com && com.allowCloseByTapBg) {
				this.__closeMessageBox(target);
			}
		} else {
			container.active = false;
		}
	}
	/**
	 * 添加导航模块，window层之上，popup层之下
	 * @param navi cc.Node
	 */
	private __addNavigator(navi: cc.Node) {
		this.__navigatorContainer.addChild(navi);
	}

	// 查找当前打开的popup
	private __findCurrentPopup(): cc.Node {
		return this.__popupContainer.childrenCount > 0 ? this.__popupContainer.children[0] : null;
	}

	// 查找当前打开的popup
	private __findCurrentPopupByClass(targetClass): cc.Node {
		if (this.__popupContainer.childrenCount > 0 && this.__popupContainer.children[0].getComponent(targetClass)) {
			return this.__popupContainer.children[0];
		} else {
			return null;
		}
	}

	// 查找当前打开的window
	private __findCurrentWindow(): cc.Node {
		return this.__windowContainer.childrenCount > 0 ? this.__windowContainer.children[0] : null;
	}

	// 查找当前打开的window
	private __findCurrentWindowByClass(targetClass): cc.Node {
		if (this.__windowContainer.childrenCount > 0 && this.__windowContainer.children[0].getComponent(targetClass)) {
			return this.__windowContainer.children[0];
		} else {
			return null;
		}
	}

	// 查找当前打开的View
	private __findCurrnetView(): cc.Node {
		return this.__viewContainer.childrenCount > 0 ? this.__viewContainer.children[0] as cc.Node : null;
	}

	// 查找当前打开的View 指定类型
	private __findCurrentViewByClass(targetClass): cc.Node {
		if (this.__viewContainer.childrenCount > 0 && this.__viewContainer.children[0].getComponent(targetClass)) {
			return this.__viewContainer.children[0] as cc.Node;
		} else {
			return null;
		}
	}



	private __removeLoadingMask(target: cc.Node) {
		if (target && target.isChildOf(this.__mainContainer)) {
			target.removeFromParent();
		}
	}

	/*
	 * 打开等待画面
	 */
	private __showLoadingMask(loading: cc.Node): cc.Node {
		this.__mainContainer.addChild(loading);
		return loading;
	}

	private __loadPrefab(prefabName: string, cb: Function) {
		cc.loader.loadRes(prefabName, (errorMessage, loadedResource) => {
			if (errorMessage) {
				cc.log('Prefab error:' + errorMessage);
				return;
			}
			if (!(loadedResource instanceof cc.Prefab)) {
				cc.log('Prefab error');
				return;
			}

			cb && cb(loadedResource);
		});
	}

	// public static hasComponentInNode(node: cc.Node, componentClass: any){
	// 	let comps = node.getComponent()

	// }

	/**
	 * 获取文本字符串模板
	 * @param key string 字符串对应的键值
	 * @param replaceValues string[] 动态替换字符串模板中的参数。模板例子：欢迎{0}来到游戏{1}区...
	 * @return 文本字符串
	 */
	public static getString(key: string, ...replaceValues: string[]): string {
		if (!Gardener.__instance.__stringResourceJson) {
			console.error('no string resource json was found...');
			return '';
		} else {
			return (replaceValues && replaceValues.length > 0) ? Gardener.__instance.__stringResourceJson[key].format(...replaceValues) : Gardener.__instance.__stringResourceJson[key];
		}
	}

	/**
	 * 判断Gardener是否初始化完成
	 */
	public static checkGgValid(): Boolean {
		if (!Gardener.__instance) {
			// egret.error('The Gardener is not initialized! \'gardener.initGardener()\' should be executed first.');
			return false;
		} else {
			return true;
		}
	}
	/**
	 * 打开一个View
	 * @param view cc.Node 打开的对象
	 * @param showAnime Boolean 是否显示切换View的过度动画
	 */
	public static openView(view: cc.Node, showAnime: Boolean = true): void {
		view.height = Gardener.__instance.__stageHeight;
		view.width = Gardener.__instance.__stageWidth;
		Gardener.__instance.__openView(view, showAnime);
	}

	/**
	 * 打开一个window，如果当前有window已经打开，则自动关闭并dispose
	 * @param window cc.Node
	 */
	public static openWindow(window: cc.Node, savePreWindow: Boolean = false): void {
		window.width = Gardener.__instance.__windowContainer.width;
		window.height = Gardener.__instance.__windowContainer.height;
		Gardener.__instance.__openWindow(window, savePreWindow, false);
	}
	/**
	 * 打开一个window，同时缓存上一个window，当此window关闭的时候，之前保存的window会自动弹回
	 * @param window cc.Node
	 */
	public static openWindowAndSavingPrevious(window: cc.Node): void {
		Gardener.__instance.__openWindow(window, true);
	}
	/**
	 * 关闭当前window，如果closeAll=true，则dispose所有之前缓存的window历史
	 * @param closeAll 默认false
	 */
	public static closeWindow(closeAll ? : Boolean): void {
		Gardener.__instance.__closeWindow(closeAll);
	}
	/**
	 * 清空并回收所有缓存的window
	 */
	public static clearWindowHistory(): void {
		Gardener.__instance.__clearWindowHistory();
	}
	/**
	 * 打开一个popup
	 * @param popup cc.Node
	 */
	public static openPopup(popup: cc.Node, showAnime: Boolean = true): void {
		Gardener.__instance.__openPopup(popup, showAnime);
	}
	/**
	 * 关闭当前popup
	 * @param immediate Boolean 立即关闭，不显示动画，默认false
	 */
	public static closePopup(immediate ? : Boolean): void {
		Gardener.__instance.__closePopup(immediate);
	}
	/**
	 * 弹出messagebox
	 * @param messageBox IMessageBox
	 * @param closeOthers Boolean 打开的同时是否关闭其他当前打开
	 */
	public static showMessageBox(messageBox: cc.Node, closeOthers:boolean = false, customDisplay:boolean = false): void {
		Gardener.__instance.__showMessageBox(messageBox, closeOthers, customDisplay);
	}
	/**
	 * 关闭所有已打开的messagebox
	 */
	public static closeAllMessageBoxes(): void {
		Gardener.__instance.__closeMessageBox(null, true);
	}
	/**
	 * 关闭指定的messagebox
	 * @param target IMessageBox
	 */
	public static closeMessageBox(target: cc.Node): void {
		Gardener.__instance.__closeMessageBox(target);
	}
	/**
	 * 显示飘字文本
	 * @param tip IFloatTip
	 */
	public static showFloatTip(tipName: string, tipContent: string, x ? : number, y ? : number): void {
		Gardener.__instance.__loadPrefab(tipName, (prefab: cc.Prefab) => {
			var tipControl: cc.Node = cc.instantiate(prefab);
			var tipCom: IGardenerFloatTip = < any > (tipControl.getComponent(GardenerFloatTip));
			tipCom.showTipContent(tipContent);
			Gardener.__instance.__showFloatTip(tipControl, x, y);
		});
	}
	/**
	 * 添加导航模块，window层之上，popup层之下
	 * @param navi cc.Node
	 */
	public static addNavigator(navi: cc.Node): void {
		Gardener.__instance.__addNavigator(navi);
	}
	/**
	 * 查找当前已打开的Popup
	 * @return cc.Node
	 */
	public static findCurrentPopup(): cc.Node {
		return Gardener.__instance.__findCurrentPopup();
	}
	/**
	 * 根据类型查找当前已打开的popup，常用于判断当前打开的popup是否是指定类型，如果不是指定类型，则返回null
	 * @param targetClass class
	 * @return cc.Node
	 */
	public static findCurrentPopupByClass(targetClass): cc.Node {
		return Gardener.__instance.__findCurrentPopupByClass(targetClass);
	}
	/**
	 * 查找当前已打开的window
	 * @return cc.Node
	 */
	public static findCurrentWindow(): cc.Node {
		return Gardener.__instance.__findCurrentWindow();
	}
	/**
	 * 根据类型查找当前已打开的window，常用于判断当前打开的window是否是指定类型，如果不是指定类型，则返回null
	 * @param targetClass class
	 * @return cc.Node
	 */
	public static findCurrentWindowByClass(targetClass): cc.Node {
		return Gardener.__instance.__findCurrentWindowByClass(targetClass);
	}
	/**
	 * 查找当前已打开的view
	 * @return cc.Node
	 */
	public static findCurrentView(): cc.Node {
		return Gardener.__instance.__findCurrnetView();
	}
	/**
	 * 根据类型查找当前已打开的view，常用于判断当前打开的view是否是指定类型，如果不是指定类型，则返回null
	 * @param targetClass class
	 * @return cc.Node
	 */
	public static findCurrentViewByClass(targetClass): cc.Node {
		return Gardener.__instance.__findCurrentViewByClass(targetClass);
	}
	/**
	 * 显示全局遮罩, 覆盖全屏，最上层
	 * @param loading cc.Node
	 */
	public static showLoadingMask(loading ? : cc.Node): cc.Node {
		loading = loading || DefaultLoadingMask.createDefaultRequestLoadingMask();
		Gardener.__instance.__showLoadingMask(loading);
		return loading
	}
	/**
	 * 移除遮罩
	 * @param loading cc.Node 指定对象
	 */
	public static removeLoadingMask(loading: cc.Node): void {
		loading.emit('removed_from_parent');
		Gardener.__instance.__removeLoadingMask(loading);
	}

	/**
	 * 加载预制体
	 * @param prefabName 
	 * @param cb 
	 */
	public static loadPrefab(prefabName: string, cb: Function): void {
		Gardener.__instance.__loadPrefab(prefabName, cb);
	}

	/**
	 * 加载view
	 * @param viewName 
	 * @param cb 
	 */
	public static loadView(viewName: string, cb: Function): void {
		Gardener.__instance.__loadPrefab(viewName, cb);
	}

	/**
	 * 加载window
	 * @param windowName 
	 * @param cb 
	 */
	public static loadWindow(windowName: string, cb: Function): void {
		Gardener.__instance.__loadPrefab(windowName, cb);
	}

	/**
	 * 加载popup
	 * @param popupName 
	 * @param cb 
	 */
	public static loadPopup(popupName: string, cb: Function): void {
		Gardener.__instance.__loadPrefab(popupName, cb);
	}

	/**
	 * 加载messagebox
	 * @param msgBoxName 
	 * @param cb 
	 */
	public static loadMessageBox(msgBoxName: string, cb: Function): void {
		Gardener.__instance.__loadPrefab(msgBoxName, cb);
	}

	/**
	 * 全局Main对象
	 */
	public static get mainContainer(): cc.Node {
		return Gardener.__instance.__mainContainer;
	}
	/**
	 * View层容器，游戏最底层
	 */
	public static get viewContainer(): cc.Node {
		return Gardener.__instance.__viewContainer;
	}
	/**
	 * Window层容器，Sprite类型，可以设置底色，在View层之上，导航层之下
	 */
	public static get windowContainer(): cc.Node {
		return Gardener.__instance.__windowContainer;
	}
	/**
	 * 导航层，在View,Window层之上，popup层之下
	 */
	public static get navigatorContainer(): cc.Node {
		return Gardener.__instance.__navigatorContainer;
	}
	/**
	 * Popup层，在导航层之上，message层之下
	 */
	public static get popupContainer(): cc.Node {
		return Gardener.__instance.__popupContainer;
	}
	/**
	 * messagebox 层，Popup层之上
	 */
	public static get messageContainer(): cc.Node {
		return Gardener.__instance.__messageContainer;
	}
	/**
	 * 全局高度
	 */
	public static get stageHeight(): number {
		return Gardener.__instance.__stageHeight;
	}
	/**
	 * 全局宽度
	 */
	public static get stageWidth(): number {
		return Gardener.__instance.__stageWidth;
	}
	/**
	 * gardener 全局配置
	 */
	public static get configuration(): number {
		return Gardener.__instance.__configuration;
	}
	/**
	 * 字符串模板json对象
	 */
	public static get stringResourceJson(): any {
		return Gardener.__instance.__stringResourceJson;
	}
	/**
	 *  window历史列表，不包含当前当前打开的window
	 */
	public static get windowHistoryList(): Array < cc.Node > {
		return Gardener.__instance.__windowHistoryList;
	}

	/********************************dragonbones***************************** */
	/**
	 * 缓存龙骨动画资源
	 */
	public static dragonbonesResDictionary: any = {};

	/**
	 * 生成龙骨二进制动画
	 * @param path string 资源文件夹路径
	 * @param armatureName string 骨架名称 （Armature）
	 * @param cb 回调函数 cb(dragonBones.ArmatureDisplay, err)
	 * @param cacheRes 是否缓存加载完成的资源
	 * @param group string 动画组，当cacheRes = true的时候，可以根据动画组集中销毁龙骨资源
	 * @param destroyAfterComplete boolean 当动画播放周期结束，是否自动销毁动画对象
	 */
	public static dragonbonesGenerateAnime(animeNode: cc.Node, path: string, armatureName: string, cb: Function, group: string = 'default', cacheRes: boolean = true, destroyAfterComplete: boolean = true) {

		let fn = (dbEntity: DragonbonesResEntity, err ? : Error) => {
			if (err) {
				cb && cb(null, err);
			} else {
				let animationDisplay: dragonBones.ArmatureDisplay = animeNode.addComponent(dragonBones.ArmatureDisplay);
				animationDisplay.name = path;
				animationDisplay.dragonAsset = dbEntity.dragonAsset;
				animationDisplay.dragonAtlasAsset = dbEntity.dragonAtlasAsset;
				animationDisplay.armatureName = armatureName;

				// 动画播放完毕，自动移除销毁
				destroyAfterComplete && animationDisplay.once(dragonBones.EventObject.COMPLETE, Gardener._destroyDbAnimeAfterComplete.bind(animationDisplay), this);

				cb && cb(animationDisplay);
			}
		}

		if (Gardener.dragonbonesResDictionary.hasOwnProperty(path)) {
			// 已缓存
			fn(Gardener.dragonbonesResDictionary[path]);
		} else {
			// 需要加载
			cc.loader.loadResDir(path, function (err, assets) {

				if (err || assets.length <= 0) {
					console.error('no dragonbones assets found - ' + path);
					fn && fn(null, err);
				} else {
					let dbEntity: DragonbonesResEntity = new DragonbonesResEntity(assets);
					assets = null;
					if (cacheRes) {
						dbEntity.group = group;
						Gardener.dragonbonesResDictionary[path] = dbEntity;
					}
					fn(dbEntity);
					// animationDisplay.armatureName = armatureName;
					// animationDisplay.playAnimation(newAnimation, playTimes);
					// animationDisplay.addEventListener(dragonBones.EventObject.COMPLETE, completeCallback);
					// animationDisplay.armature.
				}
			})
		}
	}

	/**
	 * 动画完成时，将node移除
	 */
	private static _destroyDbAnimeAfterComplete(e: dragonBones.EventObject) {
		let self: any = this;
		if (self.node && self.node.isValid) {
			self.node.destroy();
		}
		self = null;
	}

	/**
	 * 根据资源名回收龙骨资源
	 * @param pathName 龙骨资源名
	 */
	public static disposeDragonbonesAssetsByPath(pathName: string) {
		let dictionary = Gardener.dragonbonesResDictionary;
		if (dictionary.hasOwnProperty(pathName)) {
			let item: DragonbonesResEntity = dictionary[pathName];
			item.release();
			delete dictionary[pathName];
		}
	}

	/**
	 * 根据资源组回收龙骨资源
	 * @param group 龙骨资源组
	 */
	public static disposeDragonbonesAssetsByGroup(group: string) {
		let dictionary = Gardener.dragonbonesResDictionary;
		for (let key in dictionary) {
			if (dictionary.hasOwnProperty(key)) {
				let item: DragonbonesResEntity = dictionary[key];
				if (item.group === group) {
					item.release();
					delete dictionary[key];
				}
			}
		}
	}

	/**
	 * 根据资源组回收龙骨资源
	 */
	public static disposeAllDragonbonesAssets() {
		let dictionary = Gardener.dragonbonesResDictionary;
		for (let item of dictionary) {
			item.release();
		}
		dictionary = {};
	}

	/**
	 * 加载单张图片
	 * @param {String} uri 加载图片地址
	 * @param {*} callback  加载完成回调方法
	 * @param {*} thisObj  回调方法的this对象
	 * @param {*} imgMap 图片缓存map
	 */
	public static loadImage(uri: string, cb, type: string = 'jpg') {
		if (uri.substr(0, 4) === 'http') {
			cc.loader.load({
				// url: 'https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83epdccIhDxfe4nD9SibCf6MW8tJm63a9KVAibqRRibKV6Bb3Ax53dPmaib7d90icoXAV5ERiaRrY1YDHlAcA/132',
				// url: uri + '?v=' + Math.random(),
				url: uri,
				type
			}, (err, frame) => {
				if (err) {
					console.error('error to loadRes: ' + uri + ', ' + err || err.message);
					return;
				}
				let spriteFrame = new cc.SpriteFrame(frame);
				cb && cb(spriteFrame, uri);
				// 自动释放SpriteFrame和关联Texture资源
				cc.loader.setAutoReleaseRecursively(frame, true);
			});
		} else {
			cc.loader.loadRes(uri, cc.SpriteFrame, (err, frame) => {
				if (err) {
					console.error('error to loadRes: ' + uri + ', ' + err || err.message);
					return;
				}
				cb && cb(frame, uri);
				cb = null;
				// 自动释放SpriteFrame和关联Texture资源
				cc.loader.setAutoReleaseRecursively(frame, true);
			});
		}
	}

	/**
	 * 加载单张图片
	 * @param {String} uri 加载图片地址
	 * @param {*} callback  加载完成回调方法
	 * @param {*} thisObj  回调方法的this对象
	 * @param {*} imgMap 图片缓存map
	 */
	public static loadAudio(uri: string, cb, type: string = 'mp3') {
		if (uri.substr(0, 4) === 'http') {
			cc.loader.load({
				// url: 'https://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83epdccIhDxfe4nD9SibCf6MW8tJm63a9KVAibqRRibKV6Bb3Ax53dPmaib7d90icoXAV5ERiaRrY1YDHlAcA/132',
				// url: uri + '?v=' + Math.random(),
				url: uri,
				type
			}, (err, audio: cc.AudioClip) => {
				if (err) {
					console.error('error to loadRes: ' + uri + ', ' + err || err.message);
					return;
				}
				cb && cb(audio, uri);
			});
		} else {
			cc.loader.loadRes(uri, cc.AudioClip, (err, audio) => {
				if (err) {
					console.error('error to loadRes: ' + uri + ', ' + err || err.message);
					return;
				}
				cb && cb(audio, uri);
			});
		}
	}
}


/****************************** display *****************************/
export interface IGardenerFloatTip extends cc.Object {
	showTipContent(content: string);
	dispose();
}
/**
 * 飘字消息
 */
export class GardenerFloatTip extends cc.Component {

	public constructor() {
		super();
	}
	// override to show tip content
	setTipContent(content: string) {}
	dispose() {}
}

export interface IGardenerMessageBox extends cc.Object {
	showMessage(msg: any);
	display();
	dispose();
}
/**
 * 弹窗消息
 */
export class GardenerMessageBox extends cc.Component {

	// 点击背景是否允许关闭弹窗
	public allowCloseByTapBg: boolean = false;

	// 弹出多个时候是否偏移显示
	public allowMultipleOffsetPosition: boolean = true;

	public constructor(allowCloseBuyTapBg: boolean = false) {
		super();

		this.allowCloseByTapBg = allowCloseBuyTapBg;
	}

	public dispose() {}

	public display() {}

}

export interface IGardenerPopup extends cc.Object {
	display();
	dispose();
}

/**
 * 弹出窗口基类
 */
export class GardenerPopup extends cc.Component {

	// 初始化完成
	protected _isInitCompleted: boolean = false;

	public removedFromeStageHandler = null;
	public addedToStageHandler = null;

	public constructor() {
		super();
	}

	public display() {

	}

	public dispose() {
		this.removedFromeStageHandler = null;
		this.addedToStageHandler = null;
	}
}

export interface IGardenerView extends cc.Object {
	display();
	dispose();
}

/**
 * 场景
 */
export class GardenerView extends cc.Component {

	// 是否初始化完成
	protected _isInitCompleted: Boolean = false;

	public constructor() {
		super();
	}

	public display() {

	}

	public dispose() {

	}
}

export interface IGardenerWindow extends cc.Object {
	display();
	dispose();
}

/**
 * 窗口基类
 */
export class GardenerWindow extends cc.Component {

	// 初始化完成
	protected _isInitCompleted: boolean = false;

	public constructor() {
		super();

	}

	public display() {

	}

	public dispose() {

	}
}


class DefaultLoadingMask {
	public static createDefaultRequestLoadingMask(): cc.Node {
		let maskNode = new cc.Node();
		maskNode.width = Gardener.stageWidth;
		maskNode.height = Gardener.stageHeight;
		maskNode.addComponent(cc.BlockInputEvents);

		let timeoutId = 0;
		let close = () => {
			// GameLogger.debug('close loading');
			clearTimeout(timeoutId);
			maskNode.hasEventListener('removed_from_parent') && maskNode.off('removed_from_parent', close, maskNode);
			if (maskNode.isValid)
				maskNode.destroy();
		}

		maskNode.once('removed_from_parent', close, maskNode);

		let anime = () => {
			let rightOrigin = 45;
			let leftOrigin = -45;

			let right = new cc.Node();
			right.width = right.height = 44;
			right.x = rightOrigin;
			right.y = 0;

			let rightGraphics: cc.Graphics = right.addComponent(cc.Graphics);
			rightGraphics.circle(0, 0, 22);
			rightGraphics.fillColor = new cc.Color(255, 12, 12, 180);
			rightGraphics.fill();
			maskNode.addChild(right);

			let left = new cc.Node();
			left.width = left.width = 44;
			left.x = leftOrigin;
			left.y = 0;

			let leftGraphics: cc.Graphics = left.addComponent(cc.Graphics);
			leftGraphics.circle(0, 0, 22);
			leftGraphics.fillColor = new cc.Color(12, 12, 255, 180);
			leftGraphics.fill();
			maskNode.addChild(left);

			let swap = () => {
				let leftZIndex = left.getSiblingIndex();
				let rightZIndex = right.getSiblingIndex();
				if (leftZIndex > rightZIndex) {
					left.setSiblingIndex(0);
				} else {
					right.setSiblingIndex(0);
				}
			};

			let t = cc.tween;
			t(left).parallel(
				t().to(1, {
					position: cc.v3(rightOrigin)
				}, {
					easing: 'quadInOut'
				}).call(swap)
				.to(1, {
					position: cc.v3(leftOrigin)
				}, {
					easing: 'quadInOut'
				}).call(swap),

				t().to(.5, {
					scale: 1.3
				}, {
					easing: 'quadIn'
				}).to(.5, {
					scale: 1
				}, {
					easing: 'quadOut'
				})
				.to(.5, {
					scale: .7
				}, {
					easing: 'quadIn'
				}).to(.5, {
					scale: 1
				}, {
					easing: 'quadOut'
				})
			).repeatForever().start();

			t(right).parallel(
				t().to(1, {
					position: cc.v3(leftOrigin)
				}, {
					easing: 'quadInOut'
				})
				.to(1, {
					position: cc.v3(rightOrigin)
				}, {
					easing: 'quadInOut'
				}),

				t().to(.5, {
					scale: .7
				}, {
					easing: 'quadIn'
				}).to(.5, {
					scale: 1
				}, {
					easing: 'quadOut'
				})
				.to(.5, {
					scale: 1.3
				}, {
					easing: 'quadIn'
				}).to(.5, {
					scale: 1
				}, {
					easing: 'quadOut'
				})
			).repeatForever().start();

			// timeoutId = setTimeout(() => {
			// 	left.stopAllActions();
			// 	right.stopAllActions();
			// 	close();
			// }, 15000); 
		}
		timeoutId = setTimeout(anime, 1000);

		return maskNode;
	}
}
/**
 * 龙骨资源对象
 */
class DragonbonesResEntity {
	public dragonAsset: dragonBones.DragonBonesAsset;
	public dragonAtlasAsset: dragonBones.DragonBonesAtlasAsset;
	public group: string;
	public assets: cc.Asset[];
	constructor(assets: cc.Asset[], group ? : string) {

		for (let asset of assets) {
			if (asset instanceof dragonBones.DragonBonesAsset) {
				this.dragonAsset = asset;
			}
			if (asset instanceof dragonBones.DragonBonesAtlasAsset) {
				this.dragonAtlasAsset = asset;
			}
		}
		this.assets = assets;
		this.group = group;
	}

	public release() {
		if (this.assets) {
			cc.loader.release(this.assets);
		}
		this.dragonAsset = null;
		this.dragonAtlasAsset = null;
		this.assets = null;
	}
}